<template>
  <div class="tinymce-container" :style="{ width: containerWidth }">
    <textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }"></textarea>
  </div>
  <span id="error-panel" v-html="errorMsg"></span>
</template>

<script lang="ts" setup>
  import type { Editor, RawEditorSettings } from 'tinymce';
  import tinymce from 'tinymce/tinymce';
  import 'tinymce/themes/silver';
  import 'tinymce/icons/default/icons';
  import 'tinymce/plugins/advlist';
  import 'tinymce/plugins/anchor';
  import 'tinymce/plugins/autolink';
  import 'tinymce/plugins/autosave';
  import 'tinymce/plugins/code';
  import 'tinymce/plugins/codesample';
  import 'tinymce/plugins/directionality';
  import 'tinymce/plugins/fullscreen';
  import 'tinymce/plugins/hr';
  import 'tinymce/plugins/insertdatetime';
  import 'tinymce/plugins/link';
  import 'tinymce/plugins/lists';
  import 'tinymce/plugins/media';
  import 'tinymce/plugins/nonbreaking';
  import 'tinymce/plugins/noneditable';
  import 'tinymce/plugins/pagebreak';
  import 'tinymce/plugins/paste';
  import 'tinymce/plugins/preview';
  import 'tinymce/plugins/print';
  import 'tinymce/plugins/save';
  import 'tinymce/plugins/searchreplace';
  import 'tinymce/plugins/spellchecker';
  import 'tinymce/plugins/tabfocus';
  import 'tinymce/plugins/table';
  import 'tinymce/plugins/template';
  import 'tinymce/plugins/textpattern';
  import 'tinymce/plugins/visualblocks';
  import 'tinymce/plugins/visualchars';
  import 'tinymce/plugins/wordcount';
  import 'tinymce/plugins/image';
  import 'tinymce/plugins/imagetools';
  import 'tinymce/plugins/table';
  import { DefaultToolbar, DefaultPlugins } from './tinymce';
  import {
    PropType,
    ref,
    computed,
    unref,
    watch,
    onMounted,
    nextTick,
    onBeforeUnmount,
    useAttrs,
  } from 'vue';
  import { buildShortUUID } from '/@/hook/useUUID';
  import { bindHandlers } from './helper';
  // import ImgUploadPlugin from './img-upload-plugin.vue';
  import CommonApi from '/@/api/api-interface/common';
  import { formatUrl } from '/@/util/format-url';
  import { HttpClient } from '/@/api/http-client';

  const props = defineProps({
    options: {
      type: Object as PropType<Partial<RawEditorSettings>>,
      default() {
        return {};
      },
    },
    value: {
      type: String,
    },
    toolbar: {
      type: Array as PropType<string[]>,
      default: DefaultToolbar,
    },
    plugins: {
      type: Array as PropType<string[]>,
      default: DefaultPlugins,
    },
    modelValue: {
      type: String,
    },
    height: {
      type: [Number, String] as PropType<string | number>,
      required: false,
      default: 400,
    },
    width: {
      type: [Number, String] as PropType<string | number>,
      required: false,
      default: 'auto',
    },
    showImageUpload: {
      type: Boolean,
      default: true,
    },
    menubar: {
      type: String,
      default: 'file edit insert view format table',
    },
  });

  const emits = defineEmits(['change', 'update:modelValue', 'inited', 'init-error']);

  const editorRef = ref<Editor>();
  const fullscreen = ref(false);
  const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
  const elRef = ref();
  const errorMsg = ref();
  // const tinymceContent = computed(() => props.modelValue);
  const containerWidth = computed(() => {
    if (typeof props.width === 'number') {
      return `${props.width}px`;
    }
    return props.width;
  });
  let attrs = useAttrs();

  const initOptions = computed((): RawEditorSettings => {
    const { height, options, toolbar, plugins, menubar } = props;
    if (!toolbar[1].includes('errorCorrect')) {
      toolbar[1] = toolbar[1] + ' ' + 'errorCorrect';
    }
    // console.log(toolbar);
    const publicPath = import.meta.env.VITE_PUBLIC_PATH;
    return {
      selector: `#${unref(tinymceId)}`,
      height,
      toolbar,
      menubar,
      plugins,
      language_url: publicPath + '/resource/tinymce/langs/zh_CN.js',
      language: 'zh_CN',
      branding: false,
      default_link_target: '_blank',
      link_title: true,
      rel_list: false,
      target_list: false,
      object_resizing: true,
      auto_focus: true,
      skin: 'oxide',
      paste_data_images: true,
      skin_url: publicPath + '/resource/tinymce/skins/ui/oxide',
      content_css: publicPath + '/resource/tinymce/skins/ui/oxide/content.min.css',
      // icons_url: '', // 自定义图标
      images_upload_handler: (blobInfo: any, successFun: Function, failFun: Function) =>
        customImgUpload(blobInfo, successFun, failFun),
      relative_urls: false, // 图片路径问题
      onvert_urls: false,
      // statusbar: false, // 隐藏下面的状态栏
      ...options,
      setup: (editor: Editor) => {
        editor.ui.registry.addButton('errorCorrect', {
          icon: 'warning',
          tooltip: '文本纠错',
          onAction: () => {
            errorCorrect(editor.selection.getContent({ format: 'text' }));
            // emits('change', editor.getContent());
          },
        });
        editorRef.value = editor;
        editor.on('init', (e) => initSetup(e));
      },
      content_style:
        "body{font-family:'Microsoft YaHei';font-size:14px;color:#333333;line-height:1.5714285714285714;font-weight: 400;",
      // content_style:{
      //   "body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; color: #333333; line-height: 1.6;" }
      // }
    };
  });

  watch(
    () => attrs.disabled,
    () => {
      const editor = unref(editorRef);
      if (!editor) {
        return;
      }
      (editor as any).setMode(attrs.disabled ? 'readonly' : 'design');
    },
  );

  onMounted(() => {
    if (!initOptions.value.inline) {
      tinymceId.value = buildShortUUID('tiny-vue');
    }
    nextTick(() => {
      setTimeout(() => {
        initEditor();
      }, 30);
    });
  });

  onBeforeUnmount(() => {
    if (tinymce !== null) {
      tinymce?.remove?.(unref(initOptions).selector!);
    }
  });

  function initEditor() {
    const el = unref(elRef);
    if (el) {
      el.style.visibility = '';
    }
    tinymce
      .init(unref(initOptions))
      .then((editor) => {
        emits('inited', editor);
      })
      .catch((err) => {
        emits('init-error', err);
      });
  }

  function initSetup(e: {
    readonly type: string;
    readonly target: any;
    readonly isDefaultPrevented: () => boolean;
    readonly preventDefault: () => void;
    readonly isPropagationStopped: () => boolean;
    readonly stopPropagation: () => void;
    readonly isImmediatePropagationStopped: () => boolean;
    readonly stopImmediatePropagation: () => void;
  }) {
    const editor = unref(editorRef);
    if (!editor) {
      return;
    }
    const value = props.modelValue || '';

    editor.setContent(value);
    bindModelHandlers(editor);
    bindHandlers(e, attrs, unref(editorRef));
  }

  function setValue(editor: Recordable, val: string, prevVal?: string) {
    if (
      editor &&
      typeof val === 'string' &&
      val !== prevVal &&
      val !== editor.getContent({ format: attrs.outputFormat })
    ) {
      editor.setContent(val);
    }
  }

  function bindModelHandlers(editor: any) {
    const modelEvents = attrs.modelEvents ? attrs.modelEvents : null;
    const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents;

    watch(
      () => props.modelValue,
      (val, prevVal) => {
        setValue(editor, val as string, prevVal);
      },
    );

    watch(
      () => props.value,
      (val, prevVal) => {
        setValue(editor, val as string, prevVal);
      },
      {
        immediate: true,
      },
    );

    editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
      const content = editor.getContent({ format: attrs.outputFormat });
      emits('update:modelValue', content);
      emits('change', content);
      // console.log('纯文本内容', editor.getContent({ format: 'text' }));
    });

    editor.on('FullscreenStateChanged', (e: any) => {
      fullscreen.value = e.state;
    });
  }

  // 使用自定义的图片上传路径
  async function customImgUpload(blobInfo: any, successFun: Function, failFun: Function) {
    const file = blobInfo.blob();
    const formData = new FormData();
    formData.append('file', file);
    try {
      const { code, fileName } = await CommonApi.uploadFile(formData);
      const imgUrl = formatUrl(fileName);
      code === 200 ? successFun(imgUrl) : failFun('请求失败，错误代码', code);
    } catch {
      failFun('请求出错');
    }
  }

  // 根据下标替换字符
  function replaceStr(str, indexArr) {
    const strAry = str.split('');
    indexArr.forEach((ele) => {
      strAry[ele] = `<span class="err">${strAry[ele]}</span>`;
    });
    return strAry.join('');
  }
  function errorCorrect(content: String) {
    const params = {
      text: content,
    };
    HttpClient.post(`${(window as any).config.errorURL}`, params).then((res) => {
      const result = res;
      console.log(res);
      if (result.correctionResults.length) {
        errorMsg.value = result.correctionResults
          .map((ele) => {
            ele.source = replaceStr(
              ele.source,
              ele.errors.map((item) => item[2]),
            );
            return ele.source + ele.errors;
          })
          .join('<br />');
      } else {
        errorMsg.value = '';
      }
    });
  }
</script>

<style lang="less">
  // .tinymce-container: ~'@{namespace}-tinymce-container';

  .tinymce-container {
    position: relative;
    line-height: normal;

    textarea {
      z-index: -1;
      visibility: hidden;
    }
  }

  .tox .tox-tbtn svg {
    fill: #307eeb !important;
  }
  #error-panel {
    .err {
      color: red;
    }
  }
</style>
<style lang="less" scoped></style>
